﻿// -----------------------------------------------------------------------
// <copyright file="SsiParser.cs" company="Softserve">
// easy Mode SSIParcer
// </copyright>
// -----------------------------------------------------------------------
namespace WebServer.BusinessLogic.Helpers
{
    using System;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;

    /// <summary>
    /// Class which process ssi comands
    /// </summary>
    public class SsiParser
    {
        /// <summary>
        /// Short name of a day
        /// </summary>
        private const string TimefmtShortDay = "<!--#config sizefmt=\"%a\"-->";
        
        /// <summary>
        /// Full name of a day
        /// </summary>
        private const string TimefmtFullDay = "<!--#config sizefmt=\"%A\"-->";
        
        /// <summary>
        /// Short name of a month
        /// </summary>
        private const string TimefmtShortMonth = "<!--#config sizefmt=\"%b\"-->";
        
        /// <summary>
        /// Full name of a month
        /// </summary>
        private const string TimefmtFullMonth = "<!--#config sizefmt=\"%B\"-->";
        
        /// <summary>
        /// Day of a month
        /// </summary>
        private const string TimefmtDayOfMonth1 = "<!--#config sizefmt=\"%d\"-->";
        
        /// <summary>
        /// dd mm yyyy
        /// </summary>
        private const string TimefmtFormatDayMonthYear = "<!--#config sizefmt=\"%D\"-->";
        
        /// <summary>
        /// day of a month
        /// </summary>
        private const string TimefmtDayOfMonth2 = "<!--#config sizefmt=\"%e\"-->";
        
        /// <summary>
        /// Hours in 24 format
        /// </summary>
        private const string TimefmtHoursIn24Format = "<!--#config sizefmt=\"%H\"-->";
        
        /// <summary>
        /// Hours in 12 format
        /// </summary>
        private const string TimefmtHoursIn12Format = "<!--#config sizefmt=\"%I\"-->";
        
        /// <summary>
        /// Day of the year
        /// </summary>
        private const string TimefmtDayOfYear = "<!--#config sizefmt=\"%j\"-->";
        
        /// <summary>
        /// Number of a month
        /// </summary>
        private const string TimefmtNumberOfMonth = "<!--#config sizefmt=\"%m\"-->";
        
        /// <summary>
        /// Minutes in format xx
        /// </summary>
        private const string TimefmtMinutes = "<!--#config sizefmt=\"%M\"-->";
        
        /// <summary>
        /// Show AM or PM
        /// </summary>
        private const string TimefmtAmPm = "<!--#config sizefmt=\"%p\"-->";
        
        /// <summary>
        /// time in format hh mm ss a/pm
        /// </summary>
        private const string TimefmtFormatHourMinSecAp = "<!--#config sizefmt=\"%r\"-->";
        
        /// <summary>
        /// Seconds in format xx
        /// </summary>
        private const string TimefmtSeconds = "<!--#config sizefmt=\"%S\"-->";
        
        /// <summary>
        /// Time in seconds since 1.1.1970
        /// </summary>
        private const string TimefmtTimeInSeconds = "<!--#config sizefmt=\"%s\"-->";
        
        /// <summary>
        /// time in format hh mm ss
        /// </summary>
        private const string TimefmtFormatHourMinSec = "<!--#config sizefmt=\"%T\"-->";
        
        /// <summary>
        /// Week of the year
        /// </summary>
        private const string TimefmtWeekOfYear = "<!--#config sizefmt=\"%U\"-->";
        
        /// <summary>
        /// Number of a day of the tear
        /// </summary>
        private const string TimefmtNumberDayOfYear = "<!--#config sizefmt=\"%w\"-->";
        
        /// <summary>
        /// Year in format xx
        /// </summary>
        private const string TimefmtShortYear = "<!--#config sizefmt=\"%y\"-->";
        
        /// <summary>
        /// Year in format xxxx
        /// </summary>
        private const string TimefmtFullYear = "<!--#config sizefmt=\"%Y\"-->";
        
        /// <summary>
        /// Timezone in world format
        /// </summary>
        private const string TimefmtTimeZone = "<!--#config sizefmt=\"%a\"-->";
        
        /// <summary>
        /// Line what we change
        /// </summary>
        private string line;

        /// <summary>
        /// Initializes a new instance of the <see cref="SsiParser"/> class.
        /// </summary>
        public SsiParser()
        {
            this.line = null;
        }

        /// <summary>
        /// Looking for SSI comands and replacing them
        /// </summary>
        /// <param name="text">string of text answer</param>
        /// <param name="req">http request</param>
        /// <returns>text with replacing comands</returns>
        public string ParseSsi(string text, Request req)
        {
            this.line = text;

            // trying find time comands
            if (this.line.Contains("<!--#config sizefmt="))
            {
                DateTime now = DateTime.Now;
                var ci = new CultureInfo("en-US");
                this.line = this.line.Replace(TimefmtShortDay, now.ToString("ddd", ci));
                this.line = this.line.Replace(TimefmtFullDay, now.Day.ToString("dddd,ci"));
                this.line = this.line.Replace(TimefmtShortMonth, now.ToString("MMM", ci));
                this.line = this.line.Replace(TimefmtFullMonth, now.ToString("MMMM", ci));
                this.line = this.line.Replace(TimefmtDayOfMonth1, now.ToString("dd", ci));
                this.line = this.line.Replace(TimefmtFormatDayMonthYear, now.ToString("d", ci));
                this.line = this.line.Replace(TimefmtDayOfMonth2, now.ToString("dd", ci));
                this.line = this.line.Replace(TimefmtHoursIn24Format, now.ToString("HH", ci));
                this.line = this.line.Replace(TimefmtHoursIn12Format, now.ToString("hh", ci));
                this.line = this.line.Replace(TimefmtDayOfYear, now.DayOfYear.ToString(ci));
                this.line = this.line.Replace(TimefmtNumberOfMonth, now.ToString("MM", ci));
                this.line = this.line.Replace(TimefmtMinutes, now.ToString("mm", ci));
                this.line = this.line.Replace(TimefmtAmPm, now.ToString("tt", ci));
                this.line = this.line.Replace(TimefmtFormatHourMinSecAp, now.ToString("T", ci));
                this.line = this.line.Replace(TimefmtSeconds, now.ToString("ss", ci));
                var temp = new DateTime(1970, 01, 01);
                TimeSpan ti = now - temp;
                this.line = this.line.Replace(TimefmtTimeInSeconds, Math.Floor(ti.TotalSeconds).ToString(ci));
                this.line = this.line.Replace(TimefmtFormatHourMinSec, now.ToString("HH:mm:ss", ci));
                int week = Convert.ToInt32(Math.Abs(Convert.ToDouble(now.DayOfYear.ToString(ci)) / 7.0));
                this.line = this.line.Replace(TimefmtWeekOfYear, week.ToString(ci));
                this.line = this.line.Replace(TimefmtNumberDayOfYear, now.DayOfYear.ToString(ci));
                this.line = this.line.Replace(TimefmtShortYear, now.ToString("yy", ci));
                this.line = this.line.Replace(TimefmtFullYear, now.ToString("yyyy", ci));
                this.line = this.line.Replace(TimefmtTimeZone, now.ToString("O", ci));
            }

            // Some SSI Comands for AdminPage
            this.line = Regex.Replace(this.line, @"<!--#port -->", Configurator.Instance.Port.ToString());
            this.line = Regex.Replace(this.line, @"<!--#maxUsers -->", Configurator.Instance.MaxUsers.ToString());
            this.line = Regex.Replace(this.line, @"<!--#loadBalancerPort -->", Configurator.Instance.LoadBalancerPort.ToString());
            this.line = Regex.Replace(this.line, @"<!--#relativeWwwPath -->", Configurator.Instance.RelativeWwwPath.ToString());
            this.line = Regex.Replace(this.line, @"<!--#loggerPath -->", Configurator.Instance.LoggerPath.ToString());
            this.line = Regex.Replace(this.line, @"<!--#ServerMode -->", Configurator.Instance.ServerMode.ToString());

            // now working with printenv
            if (this.line.Contains("<!--#printenv"))
            {
                this.line = Regex.Replace(this.line, "<!--#printenv DOCUMENT_ROOT -->", Configurator.Instance.RelativeWwwPath.ToString());
                this.line = Regex.Replace(this.line, "<!--#printenv USER_AGENT --> -->", req.UserAgent);
                this.line = Regex.Replace(this.line, "<!--#printenv HTTP_USER_AGENT -->", req.UserAgent);
                this.line = Regex.Replace(this.line, "<!--#printenv SERVER_ADDR -->", Configurator.Instance.Host.ToString());
                this.line = Regex.Replace(this.line, "<!--#printenv DOCUMENT_URI -->", req.HttpPath);
                this.line = Regex.Replace(this.line, "<!--#printenv REQUEST_URI -->", req.HttpPath);
                this.line = Regex.Replace(this.line, "<!--#printenv SERVER_SOFTWARE -->", Configurator.Instance.ServerName);
                this.line = Regex.Replace(this.line, "<!--#printenv SERVER_ADMIN -->", "071cs@itacademy.lviv.ua");
                this.line = Regex.Replace(this.line, "<!--#printenv DATE_LOCAL -->", DateTime.Now.ToLocalTime().ToString());
                this.line = Regex.Replace(this.line, "<!--#printenv HTTP_COOKIE -->", req.CookiesRaw ?? "Null");
            }

            this.ParseInclude();
            
            return this.line;
        }

        /// <summary>
        /// Parcing include tag
        /// </summary>
        /// <returns>text after processing include tags</returns>
        private string ParseInclude()
        {
            Encoding enc = Encoding.GetEncoding(1251);

            StringBuilder strBuilder = new StringBuilder(this.line);
            MatchCollection matches = Regex.Matches(this.line, @"<!--#include file=""(.*)"" -->");
            foreach (Match m in matches)
            {
                string fileName = Regex.Replace(m.Value, @"<!--#include file=""(.*)"" -->", @"$1");
                strBuilder = strBuilder.Remove(m.Index, m.Length);

                if (fileName[0] == '\\')
                {
                    fileName = fileName.Remove(0);
                }

                fileName = Path.Combine(Configurator.Instance.RelativeWwwPath, fileName);

                string textFromFile;
                try
                {
                    textFromFile = File.ReadAllText(fileName, enc);
                }
                catch (IOException)
                {
                    continue;
                }

                strBuilder = strBuilder.Insert(m.Index, textFromFile);
            }

            return strBuilder.ToString();
        }
    }
}
